package org.example.time.util;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

/**
 * 区间工具
 *
 * @author YunKaiLuo
 * @date 2024/10/9
 */
public class TimeIntervalUtil {

    private TimeIntervalUtil() {
    }

    public static List<TimeInterval<LocalDate>>
    getListForDate(
            String start, String end,
            ClassificationOfTimeEnum classificationOfTime,
            IntervalValueStrategy strategy
    ) {
        LocalDate startDate = LocalDateUtil.deserialize(start);
        LocalDate endDate = LocalDateUtil.deserialize(end);
        final ChronoUnit CHRONO_UNIT = getChronoUnit(classificationOfTime);
        final long betweenSize = CHRONO_UNIT.between(startDate, endDate);
        List<TimeInterval<LocalDate>> list = new ArrayList<>((int) betweenSize);
        for (LocalDate tempStartDate = startDate, tempEndDate = null;
             tempStartDate.isBefore(endDate);
        ) {
            switch (strategy) {
                case EQUAL: {
                    tempEndDate = tempStartDate;
                }
                break;
                case LEFT_CLOSED_RIGHT_OPEN: {
                    tempEndDate = tempStartDate.plus(1, CHRONO_UNIT);
                }
                break;
                case LEFT_CLOSED_RIGHT_CLOSED: {
                    tempEndDate = LocalDateUtil.getStartOrEndDayOf(classificationOfTime, tempStartDate, false);
                }
                break;
                default:
                    throw new RuntimeException("Unexpected value: " + strategy);
            }
            TimeInterval<LocalDate> interval = new TimeInterval<>(tempStartDate, tempEndDate);
            list.add(interval);
            tempStartDate = tempStartDate.plus(1, CHRONO_UNIT);
        }
        return list;
    }


    public static List<TimeInterval<LocalDateTime>>
    getListForDateTime(
            String start, String end,
            ClassificationOfTimeEnum classificationOfTime,
            IntervalValueStrategy strategy
    ) {
        LocalDateTime startDate = LocalDateTimeUtil.deserialize(start);
        LocalDateTime endDate = LocalDateTimeUtil.deserialize(end);
        final ChronoUnit CHRONO_UNIT = getChronoUnit(classificationOfTime);
        final long betweenSize = CHRONO_UNIT.between(startDate, endDate);
        List<TimeInterval<LocalDateTime>> list = new ArrayList<>((int) betweenSize);
        for (LocalDateTime tempStartDate = startDate, tempEndDate = null;
             tempStartDate.isBefore(endDate);
        ) {
            switch (strategy) {
                case EQUAL: {
                    tempEndDate = tempStartDate;
                }
                break;
                case LEFT_CLOSED_RIGHT_OPEN: {
                    tempEndDate = tempStartDate.plus(1, CHRONO_UNIT);
                }
                break;
                case LEFT_CLOSED_RIGHT_CLOSED: {
                    tempEndDate = LocalDateTimeUtil.getStartOrEndDayOf(classificationOfTime, tempStartDate, false);
                }
                break;
                default:
                    throw new RuntimeException("Unexpected value: " + strategy);
            }
            TimeInterval<LocalDateTime> interval = new TimeInterval<>(tempStartDate, tempEndDate);
            list.add(interval);
            tempStartDate = tempStartDate.plus(1, CHRONO_UNIT);
        }
        return list;
    }

    private static ChronoUnit getChronoUnit(ClassificationOfTimeEnum classificationOfTime) {
        switch (classificationOfTime) {
            case YEAR:
                return ChronoUnit.YEARS;

            case MONTH:
                return ChronoUnit.MONTHS;

            case DAY:
                return ChronoUnit.DAYS;

            case HOUR:
                return ChronoUnit.HOURS;

            case MINUTE:
                return ChronoUnit.MINUTES;

            case SECOND:
                return ChronoUnit.SECONDS;

            case MILLISECOND:
                return ChronoUnit.MILLIS;

            case MICROSECOND:
                return ChronoUnit.MICROS;

            default:
                return null;
        }
    }
}
